#ifndef NodeBASE_H
#define NodeBASE_H

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <string>
#include <string.h>
#include <algorithm>
#include <ctime>

//#ifndef COMPUTEBASED
//#define COMPUTEBASED
//#endif

#ifndef USEHYBRID
#define USEHYBRID
#endif

//#ifndef CONSIDERCORR
//#define CONSIDERCORR
//#endif

//#ifndef SELECTONE
//#define SELECTONE
//#endif

#ifndef NEW
#define NEW
#endif

//#ifndef DEBUG_SIMULATE
//#define DEBUG_SIMULATE
//#endif

using namespace std;

extern int MC_NUM;
/// The types of Nodes
typedef enum signature_ {
        HYBRID,
	INPUT,
	OUTPUT,
	NOT,
	FLIPFLOP,
	NAND2,
	NAND3,
	NOR2,
	NOR3,
	UNDEFINED
} Signature;

/// The types of Values
typedef enum simvalue_ {
	Zero,
	One,
	Z,
	X
} Value;

typedef struct event_
{
        int* inout;
}event;
	
class Node;
class Signal{
  public:
        int x,y;
        int region;
        int sw1;
        Value val1;
        Signal(string name);
        /// Return ith input of the Node
        Node* getInput(int i) const {return inputs[i];}
          
        /// Return ith output of the Node
        Node* getOutput(int i) const {return outputs[i];}
		
        /// Add a node to inputs of the Node
        bool addInput(Node *g) {inputs.push_back(g); return true;}
	
        /// Add a node to outputs of the Node
        bool addOutput(Node *g) {outputs.push_back(g); return true;}
		
        /// number of inputs
        int inputSize(void) {return inputs.size();}
		
        /// number of outputs
        int outputSize(void) {return outputs.size();}
	  
        /// return name
        string const getName(void) {return name_;}
		
        /// return an string representing the signal
        string str(void) const;
		
        /// initialiaze variables
        void init(void);
		
        /// set the value of the signal
        bool setVal(Value v) { val_ = v; return true;}
		
        /// read value of the signal
        Value getVal(void) {return val_;}
		
        /// if signal value is known or not
        bool isKnown(void) {return (val_!= Z);}
		
        /// set SV1
        void setSV1(int sv) {if(sv>SV1) SV1 = sv;}
		
        /// set SV0
        void setSV0(int sv) {if(sv>SV0) SV0 = sv;}
		
        /// get SV1
        float getP1(void) {return P1;}
		
        /// get SV0
        float getP0(void) {return P0;}
		
        /// set P1
        void setP1(float p) {P1=p;}
		
        /// set P0
        void setP0(float p) {P0=p;}
		
        /// get SV1
        float getSV1(void) {return SV1;}
		
        /// get SV0
        float getSV0(void) {return SV0;}
		
        /// set FR1
        void setFR1(float fr) {FR1 = fr;}
		
        /// set FR0
        void setFR0(float fr) {FR0 = fr;}
		
        /// get SV1
        float getFR1(void) {return FR1;}
		
        /// get SV0
        float getFR0(void) {return FR0;}
		
        /// set BR1
        void setBR1(float fr) {BR1 = fr;}
		
        /// set BR0
        void setBR0(float fr) {BR0 = fr;}
		
        /// get BR1
        float getBR1(void) {return BR1;}
		
        /// get BR0
        float getBR0(void) {return BR0;}
		
        /// get R1
        float getR1(void) {return R1;}
		
        /// get R0
        float getR0(void) {return R0;}
		
        /// set V1
        void setV1(float sv) {if(sv>V1) V1 = sv;}
		
        /// set V0
        void setV0(float sv) {if(sv>V0) V0 = sv;}
		
        /// get V1
        float getV1(void) {return V1;}
		
        /// get SV0
        float getV0(void) {return V0;}
		
        /// get V1
        float getRV1(void) {return RV1;}
		
        /// get SV0
        float getRV0(void) {return RV0;}

        /// compute V1
        float computeV1(void) {V1 = max(V1,max(SV1*P1, RV1)); return V1; }
		
        /// compute V0
        float computeV0(void) {V0 = max(V0,max(SV0*P0, RV0)); return V0; }	
		
        /// compute R1
        float computeR1(void) {R1 = FR1+BR1-FR1*BR1; return R1; }	
		
        /// compute R0
        float computeR0(void) {R0 = FR0+BR0-FR0*BR0; return R0; }	
	
#ifndef CONSIDERCORR
        //so here we want to avoid the problem of inprecision,we do not
        //time the probability anymore
        float computeRV0(void) {
                if(name_ == "NOR2" || name_ == "NAND2" || name_ ==
                   "HYBRID") 
                        //if(name_ == "HYBRID") 
                        RV0 = R0;
                else
                        RV0 = R0*P0;
                return RV0; 
        }	//min 
        float computeRV1(void) {
                if(name_ == "NOR2" || name_ == "NAND2" || name_ ==
                   "HYBRID") 
                        //if(name_ == "HYBRID") 
                        RV1 = R1; 
                else
                        RV1 = R1*P1;
                return RV1; 
        }	
#else	
        /// compute R0
        float computeRV0(void) {RV0 = R0*P0; return RV0; }
        /// compute R1
        float computeRV1(void) {RV1 = R1*P1; return RV1; }	
#endif        
        /// compute P0 and P1
        void computeP(void);
		
        /// if it is traced
        bool getTraced(void) {return trace;}
		
        /// it is traced
        void setTraced(bool t) {trace = t;}
		
        /// compute restorability
        void doRestor(void);
   
        /// get index
	int getIndex(void) {return index;}

	/// set index
	void setIndex(int i) {index=i;}
		
        float getV() {return V1+V0;}
		
        void setFlag0(int f) {flag0=f;}
        void setFlag1(int f) {flag1=f;}
        int getFlag0(void) {return flag0;}
        int getFlag1(void) {return flag1;}
		
        void print(void);
        int stored;
        Value val;
        int sw;
        
  protected:
			
        // visibility stuff
        float SV1,SV0,V1,V0,P1,P0;
        float FR1,FR0,BR0,BR1,R1,R0,RV1,RV0;
        string name_;
        bool trace;
        Value val_;
        vector<Node*> inputs;
        vector<Node*> outputs;
        int index;
        int flag0,flag1;
};
	
class Node {
  public:
	/// Destructor
	virtual ~Node() {}
  Node(): visited(false),fWeight(0),bWeight(0),isCriticalFF(false),cPathCount(0) {}
	//Node(string name) : name_(name) {}
	/// The signature representing the Node type
	virtual Signature signature(void) {return UNDEFINED;}

	/// Return a string representation of the Node
	virtual string getName(void) const {return name_;}
	
	/// Return ith input of the Node
	virtual Signal* getInput(int i) const {return inputs[i];}
	
	/// Return ith output of the Node
	virtual Signal* getOutput(int i) const {return outputs[i];}
	
	/// Add a node to inputs of the Node
	virtual bool addInput(Signal *g) {inputs.push_back(g); return true;}

	/// Add a node to outputs of the Node
	virtual bool addOutput(Signal *g) {outputs.push_back(g); return true;}
	
	/// number of inputs
	virtual int inputSize(void) {return inputs.size();}
	
	/// number of outputs
	virtual int outputSize(void) {return outputs.size();}
	
	/// Return a string representation of the Signal
	virtual string str(void) const {return (string) "x";}
	
	/// if the node is active(e.g. the node can be evaluated) 
	virtual bool isActive(void) { return false;}
	
	/// set output of the node
	virtual void setOutput(void) {}
	
	/// check if visited
	virtual bool getVisited() {return visited;}
	
	/// set visited
	virtual void setVisited(bool v) {visited=v;}
	
	/// if the node is active(e.g. the node can be evaluated) 
	virtual bool isBackwardActive(void) { return false;}
	
	/// set output of the node
	virtual void setInput(void) {}
	
	/// check if visited
	virtual bool getBackwardVisited() {return backwardVisited;}
	
	/// set visited
	virtual void setBackwardVisited(bool v) {backwardVisited=v;}
	
	/// forward restoration
	virtual void forwardRestor(void) {}
	
	/// backward restoration
	virtual void backwardRestor(void) {}

	/// get index
	int getIndex(void) {return index;}

	/// set index
	void setIndex(int i) {index=i;}

        int getGateIndex() {return gate_index;}

        void setGateIndex(int i) {gate_index=i;}

	bool passive;
        
        //initialization of the correlation stat
        bool initCorStat()
        {
                for(int i=0;i<8;i++)
                        correlation_stat[i] = 0;
                return true;
        }
        
        //update the statistics of the correlation
        bool updateCorStat(int i)
        {
                correlation_stat[i]++;
                return true;
        }
        
        //find the most frequently happened pattern
        int findMajority()
        {
                int i,result,major = correlation_stat[0];
                for(i = 1; i < 8; i++ )
                        if(major < correlation_stat[i])
                        {
                                major = correlation_stat[i];
                                result = i;
                        }
                
                return major;
        }

        /// inputs
	vector<Signal*> inputs;
	/// outputs
	vector<Signal*> outputs;

  protected:
	/// The name of the Node
	string name_;        
	int correlation_stat[8];
        bool visited;
	bool backwardVisited;
	
  public:	
	// timing parameters
	double AT, ATTmp, RATTmp;
        bool isCriticalFF;  
        int cPathCount; 
	int fWeight, bWeight;
        int index;
        int gate_index;
        int level;
        int correlated;
        int HG_ID;
        int traversed;
};

class HybridNode : public Node
{
  public:
        HybridNode() {name_ = "HYBRID";}
        virtual Signature signature(void) {return HYBRID;}
        //virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);        
        
        bool initEvent()
        {
                Extra_br_event = new int[extra_events.size()];
                BR_event = new int[2 * inputs_.size()];
                FR0_event = new int[OFR0.size()];
                FR1_event = new int[OFR1.size()];
                memset(Extra_br_event, 0, extra_events.size() * sizeof(int));
                memset(BR_event, 0, 2 * inputs_.size() * sizeof(int));
                memset(FR0_event, 0, OFR0.size() * sizeof(int));
                memset(FR1_event, 0, OFR1.size() * sizeof(int));
        }
        
        list<int>::iterator findInput(int s)
        {
                return find(inputs_.begin(),inputs_.end(),s);
        }
        
        bool clearInputs()
        {
                inputs_.clear();
                return true;
        }
        
        bool addInput(int s)
        {
                inputs_.push_back(s);
                return true;
        }

        bool swapIndexes(vector<int> L)
        {
                swap(indexes, L);
                return true;
        }
        
        bool addOutput(int s)
        {
                outputs_.push_back(s);
                return true;
        }
        
        int getOutputsSize()
        {
                return outputs_.size();
        } 

        list<int>::iterator delInput(list<int>::iterator it)
        {
                return inputs_.erase(it);
        }
        
        int getInputsSize()
        {
                return inputs_.size();
        }
        
        int getInternalSize()
        {
                return internal.size();
        }
        
        int getInternal(int i)
        {
                list<int>::iterator it;
                it = internal.begin();
                int j;
                for(j = 0;j<i;j++)
                        it++;
                                
                return *it;
        }
        
        int getInput(int i)
        {
                list<int>::iterator it;
                it = inputs_.begin();
                int j;
                for(j = 0;j<i;j++)
                        it++;
                                
                return *it;
        }
        
        int getOutput(int i)
        {
                list<int>::iterator it;
                it = outputs_.begin();
                int j;
                for(j = 0;j<i;j++)
                        it++;
                                
                return *it;
        }
        
        int getIndex(int i)
        {
                return indexes[i];
        }
        
        int getIndexSize()
        {
                return indexes.size();
        }
        
        bool addIndex(int i)
        {
                indexes.push_back(i);
                return true;
        }
        
        bool clearHG()
        {
                indexes.clear();
                inputs_.clear();
                internal.clear();
                outputs_.clear();
        }
        
  public:
        int** events;//size: [2*i] * i, i is number of inputs
        vector<int> indexes;//gates inside
        list<int> inputs_;//signals
        list<int> internal;//signals
        list<int> outputs_;//signals
        vector<int*> OFR0;
        vector<int*> OFR1;
        vector<int*> extra_events;
        int* Extra_br_event;//size: extra_events.size() 
        int* BR_event;//size: 2*i, i is number of inputs
        int* FR0_event;//size: OFR0.size()
        int* FR1_event;//size: OFR1.size()
};

class InputNode : public Node {	
  public:
        InputNode() {name_ = "INPUT";}
        virtual Signature signature(void) {return INPUT;}
        virtual string str(void) const;
        virtual bool isActive(void);		
        virtual bool isBackwardActive(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
};

class OutputNode : public Node {	
  public:
        OutputNode() {name_ = "OUTPUT";}
        virtual Signature signature(void) {return OUTPUT;}
        virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);		
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
};

class NotNode : public Node {	
  public:
        NotNode() {name_ = "NOT"; }
        virtual Signature signature(void) {return NOT;}
        virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
};

class FlipflopNode : public Node {	
  public:
        FlipflopNode() {name_ = "FLIPFLOP";}
        virtual Signature signature(void) {return FLIPFLOP;}
        virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);
        virtual void setOutput(void);
        virtual void setInput(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
        virtual float getV1(void);
        virtual float getV0(void);
        virtual float getV(void);
		
        /// Add a node to outputs of the Node
        bool addqBoutput(Signal *g) {qBoutputs.push_back(g); return true;}
		
        /// number of qBoutputs
        int qBoutputSize(void) {return qBoutputs.size();}
		
        /// Return ith output of the Signal
        Signal* getqBoutput(int i) const {return qBoutputs[i];}

        int getNodeweight() {return Node_weight;}
        
        int getFFindex() {return FF_index;}
        
        bool setFFindex(int i) {FF_index = i;return true;}
        
        bool addFFfanout(int i) {fanout_FF.push_back(i);return true;}
        
        int getFFfanoutSize() {return fanout_FF.size();}
        
        int getFFfanout(int i) {return fanout_FF[i];}
        
        bool addNodeweight(){Node_weight++; return true;}
        
        /// check if it has feedback loop
        bool hasLoop(void);

        vector<int> fanout_FF;
  protected:
        /// outputs
        vector<Signal*> qBoutputs;
        //vector<int> fanout_FF_edgeW;  //better to combine two vector together
	int FF_index;
        int Node_weight;
};

class Nand2Node : public Node {	
  public:
        Nand2Node() {name_ = "NAND2";}
        virtual Signature signature(void) {return NAND2;}
        virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
        
        bool initEventStat()
        {
                event_br0[0] = 0;
                event_br0[1] = 0;
                event_fr0_c = 0;
                return true;
        }
        
  public:
        int event_br0[2];
        int event_fr0_c;
};

class Nor2Node : public Node {	
  public:
        Nor2Node() {name_ = "NOR2"; }
        virtual Signature signature(void) {return NOR2;}
        virtual string str(void) const;
        virtual bool isActive(void); 
        virtual bool isBackwardActive(void);
        virtual void forwardRestor(void);
        virtual void backwardRestor(void);
        
        bool initEventStat()
        {
                event_br1[0] = 0;
                event_br1[1] = 0;
                event_fr1_c = 0;
                return true;
        }
        
  public:
        int event_br1[2];
        int event_fr1_c;

};

class Nand3Node : public Node {	
  public:
        Nand3Node() {name_ = "NAND3";}
        virtual Signature signature(void) {return NAND3;}
        virtual string str(void) const;
	//	virtual bool isActive(void); 
	//	virtual void setOutput(void);
};

class Nor3Node : public Node {	
  public:
        Nor3Node() {name_ = "NOR3";}
        virtual Signature signature(void) {return NOR3;}
        virtual string str(void) const;
	//	virtual bool isActive(void); 
	//	virtual void setOutput(void);
};

#endif
